package storage;

import Interface.Entity;

/**
 * Developed for NTT - All is an Entity
 * EntityStorage e' la classe fulcro del progetto NTT, Questa particolare classe EntityStorage
 * appartiene allo Storage, viene utilizzata da base per le implementazioni
 * degli strati successivi.
 * 
 * @author Alessandro Pollace
 * @version 0.1
 * 
 * 
 */
public class EntityStorage implements Entity {

    /***** DATA *****/
    /**
     * Nome dell'entità
     */
    private String nameEntity;
    /**
     * Nome della chiave primaria dell'entità
     */
    private String namePrimaryKey;
    /**
     * Settato a true la cancellazione dal DB avviene in maniera logica, fisica altrimenti.
     */
    private boolean LogicalDelete;
    /**
     * Array contenente i nomi di tutti gli attributi
     */
    private String[] nameAttribute;
    /**
     * Array Contente i tipi di tutti gli attributi
     */
    private String[] typeAttribute;
    /**
     * Array contente i valori di tutti gli attributi
     */
    private Object[] valueAttribute;
    /**
     * Array che indica se un attributo è stato modificato o meno dall'ultimo caricamento da DB o dalla sua creazione
     */
    private boolean modified[];
    /**
     * Array che indica se un attributo è unico all'interno del database o meno.
     */
    private boolean unique[];
    /**
     * Array che indica se un attributo è inserito dall'utente all'atto della sua creazione o meno
     */
    private String inserible[];
    /**
     * Array che indica i gruppi di utenti per cui è possibile modificare l'attributo
     */
    private String modificable[];
    /**
     * Array che indica i gruppi di utenti per cui è possibile modificare l'attributo
     */
    private String readable[];
    /**
     * Indica l'utente cha ha instanziato in questo momento la classe Entity
     */
    protected String user;
    /**
     * Indica il gruppo dell'utente cha ha instanziato in questo momento la classe Entity
     */
    protected String group;
    /**
     * Indica il gruppo di utenti che ha il permesso di creare questo tipo di entity
     */
    private String creation;
    //******************************************************
    //**************END DATA********************************
    //******************************************************

    /**
     * Costruttore dell'entity EntityStorage
     * @param entityName
     *            Nome dell'entità da caricare
     * @return Istanza dell'entity
     */
    public EntityStorage(String entityName) throws Exception {
        ConfigLoader loader = new ConfigLoader();
        // Avvaloro la configurazione presa dal file
        this.namePrimaryKey = loader.getPrimaryKeyName(entityName);
        this.LogicalDelete = loader.isLogicaldelete(entityName);
        this.nameAttribute = loader.getAttributeName(entityName);
        this.typeAttribute = loader.getAttributeType(entityName);
        this.unique = loader.isUnique(entityName);
        this.inserible = loader.isInserible(entityName);
        this.modificable = loader.getModifyPermits(entityName);
        this.readable = loader.getReadPermits(entityName);
        this.creation = loader.howCreate(entityName);

        this.nameEntity = entityName;

        // Istanzio le variabili che avvalora l'entity stessa.
        this.valueAttribute = new Object[this.nameAttribute.length];
        this.modified = new boolean[this.nameAttribute.length];
    }

    /************************************************************************/
    /***** METHOD - PRIVATE *****/
    /**
     * Questo metotdo ritorna true se il tipo è compatibile false altrimenti
     *
     * @param index
     *            indice dell'attributo presente all'interno della classe
     * @param AttributeValue
     *            valore dell'attributo da controllare, passato dall'esterno
     * @return true se il tipo è compatibile false altrimenti
     */
    private boolean checkAttributeType(int index, Object AttributeValue) {
        TypeConverter converter = new TypeConverter();
        String s;
        try {
            s = converter.NTTToJAVA(this.typeAttribute[index]);
            if (s.equals("String") == true) {
                String a = (String) AttributeValue;
                return true;
            }
            if (s.equals("Integer") == true) {
                Integer a = (Integer) AttributeValue;
                return true;
            }
            if (s.equals("Float") == true) {
                Float a = (Float) AttributeValue;
                return true;
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }

    //******************************************************
    //**************END PRIVATE*****************************
    //******************************************************
    /***** METHOD - PROTECTED *****/
    /**
     * Questo metodo resetta i flag di modifica
     */
    protected void resetModifyFlag() {
        // DATA
        int i;
        // OPERATION
        for (i = 0; i < this.modified.length; i++) {
            this.modified[i] = false;
        }
    }

    /**
     * questo metodo ritorna un indice numerico che indica la posizione
     * dell'attributo.
     *
     * @param AttributeName
     *            Nome dell'atributo di cui si intende ritornare l'indice.
     * @return indice numerico che indica la posizione dell'attributo.
     */
    protected int getAttributeIndex(String AttributeName) {
        // DATA
        int n;
        int i;
        // OPERATION
        n = this.nameAttribute.length;
        for (i = 0; i < n; i++) {
            if (AttributeName.equals(this.nameAttribute[i]) == true) {
                return i;
            }
        }
        // ERROR
        return -1;
    }

    /**
     * Questo metodo ritorna il proprietario della entity, 
     * nel caso in cui la entity sia un utente allora il proprietario è se 
     * stesso, in caso contrario il proprietario è colui che ha creato la entity.
     * Se la entity è stata creata da un utente anonimo non ha proprietario.
     * @return String
     */
    protected String getOwner() {
        try {
            if (this.getNome().equals("User")) {
                return this.getAttributeValueNoLimitsToString("username");

            } else {
                return this.getAttributeValueNoLimitsToString("owner");
            }
        } catch (Exception ex) {
            return null;
        }

    }

    /**
     * Questo metodo permette di ottenere un attributo senza passare per i
     * permessi, in alcuni casi infatti il sistema necessita della lettura di dati
     * riservati, come ad esempio il caso del login, un utente non ancora loggato
     * ha bisogno di un dato sensibile come la password.
     * @param AttributeName Nome dell'attributo da leggere
     * @return Object
     * @throws Exception lattributo non esiste
     */
    protected Object getAttributeValueNoLimits(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeValue-AttributeNotExist");
        }
        return this.valueAttribute[index];
    }

    /**
     * Questo Metodo ritorna se l'entity in questione subisce una cancellazione
     * logica o fisica.
     *
     * @return true = cancellazione logica False= cancellazione fisica
     */
    protected boolean isLogicalDeleteEntity() {
        return this.LogicalDelete;
    }

    /**
     * Questo metodo permette sapere che gruppo di utenti ha il permesso di
     * creare questo tipo di entity
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return String
     */
    protected String howCreate() {
        return creation;
    }
    //******************************************************
    //**************END PROTECTED***************************
    //******************************************************

    /***** METHOD - PUBLIC *****/
    /**
     * Questo Metodo ritorna il nome della classe
     *
     * @return Nome dell'entity su DB
     */
    @Override
    public String getNome() {
        return this.nameEntity;
    }

    /**
     * Questo metodo ritorna il nome della PrimaryKey
     *
     * @return nome della PrimaryKey
     */
    @Override
    public String getPrymaryKeyName() {
        return this.namePrimaryKey;
    }

    /**
     * Ritorna i nomi di tutti gli attributi.
     *
     * @return Array di tutti i nomi degli attributi.
     */
    @Override
    public String[] getAllNameAttribute() {
        return this.nameAttribute;
    }

    /**
     * Ritorna i nomi di tutti gli attributi modificati dal loro caricamento.
     *
     * @return Array di tutti i nomi degli attributi modificati dal loro
     *         caricamento.
     */
    @Override
    public String[] getAllNameAttributeModified() {
        // DATA
        String mod[] = null;
        int i, totMod = 0, modTMP[];
        // OPERATION
        modTMP = new int[this.nameAttribute.length];
        for (i = 0; i < this.nameAttribute.length; i++) {
            if (this.modified[i] == true) {
                modTMP[totMod] = i;
                totMod++;
            }
        }
        mod = new String[totMod];
        for (i = 0; i < totMod; i++) {
            mod[i] = this.nameAttribute[modTMP[i]];
        }
        return mod;
    }

    /**
     * Questo metodo ritorna il valore dell'attributo passato per nome.
     *
     * @param AttributeName
     *            Nome dell'attributo da ritornare
     * @return Valore dell'attributo
     * @throws Exception
     *             Nome attributo non esistente
     */
    @Override
    public Object getAttributeValue(String AttributeName) throws Exception {
        return this.getAttributeValueNoLimits(AttributeName);
    }

    /**
     * Questo metodo ritorna il valore dell'attributo passato per nome.
     *
     * @param AttributeName
     *            Nome dell'attributo da ritornare
     * @return Valore dell'attributo
     * @throws Exception
     *             Nome attributo non esistente
     */
    @Override
    public String getAttributeValueToString(String AttributeName) throws Exception {
        return this.getAttributeValueNoLimitsToString(AttributeName);
    }

    /**
     * Questo metodo permette di ottenere un attributo senza passare per i
     * permessi, in alcuni casi infatti il sistema necessita della lettura di dati
     * riservati, come ad esempio il caso del login, un utente non ancora loggato
     * ha bisogno di un dato sensibile come la password.
     * @param AttributeName Nome dell'attributo da leggere
     * @return String
     * @throws Exception lattributo non esiste
     */
    public String getAttributeValueNoLimitsToString(String AttributeName) throws Exception {
        // DATA
        Object o;
        String s;
        // OPERATION
        o = this.getAttributeValueNoLimits(AttributeName);
        if (o == null) {
            throw new Exception(
                    "Error-Entity-getAttributeValueToString-AttributeNotInitialized");
        }
        s = o.toString();
        return s;
    }

    /**
     * Questo metodo ritorna il tipo di un attributo chiamandolo per nome.
     *
     * @param AttributeName
     *            Nome dell'attributo di cui serve il tipo.
     * @return Tipo dell'attributo
     * @throws Exception
     *             Nome attributo non esistente
     */
    @Override
    public String getAttributeType(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeType-AttributeNotExist");
        }
        return this.typeAttribute[index];
    }

    /**
     * Questo metodo setta il valore dell'attributo passato per nome.
     *
     * @param AttributeName
     *            Nome dell'attributo da settare.
     * @throws Exception
     */
    @Override
    public void setAttributeValue(String AttributeName, Object value) throws Exception {
        this.setAttributeValueSuperMode(AttributeName, value);
    }

    /**
     * Questo metodo setta il valore dell'attributo passato per nome senza restrizioni.
     *
     * @param AttributeName
     *            Nome dell'attributo da settare.
     * @throws Exception
     */
    public void setAttributeValueSuperMode(String AttributeName, Object value) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-setAttributeValue-AttributeNotExist");
        }

        if (this.checkAttributeType(index, value) == true) {
            this.valueAttribute[index] = value;
            this.modified[index] = true;
        } else {
            // ERROR
            throw new Exception(
                    "Error-Entity-setAttributeValue-InvalidAttributeType");
        }
    }

    /**
     * Questo metodo permette di capire se un attributo è inseribile o meno, in
     * oltre se un attriburto è inseribile ci dice anche se l'inserimento di
     * questo attributo è obbligatorio
     *
     * @param AttributeName
     *            Attributo su cui lavorare
     * @return Inseribile, Non inseribile, Inserimento forzato
     * @throws Exception
     */
    @Override
    public String getAttributeIsInserible(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeType-AttributeNotExist");
        }
        return this.inserible[index];
    }

    /**
     * Questo metodo ritorna i tipi di Utenti per cui un attributo è modificabile
     *
     * @param AttributeName
     *            Attributo su cui lavorare
     * @return Inseribile, Non inseribile, Inserimento forzato
     * @throws Exception
     */
    public String[] getAttributePermitsModify(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeType-AttributeNotExist");
        }
        return this.modificable[index].split("/");
    }

    /**
     * Questo metodo ritorna i tipi di Utenti per cui un attributo è leggibile
     *
     * @param AttributeName
     *            Attributo su cui lavorare
     * @return Inseribile, Non inseribile, Inserimento forzato
     * @throws Exception
     */
    @Override
    public String[] getAttributePermitsReading(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeType-AttributeNotExist");
        }
        return this.readable[index].split("/");
    }

    /**
     * Questo metodo permette di vedere se un attributo deve essere unico o meno
     * all'interno del DB
     *
     * @param AttributeName
     *            Attributo su cui si vuole lavorare
     * @return
     * @throws Exception
     */
    @Override
    public boolean getAttributeIsUnique(String AttributeName) throws Exception {
        // DATA
        int index;
        // OPERATION
        index = this.getAttributeIndex(AttributeName);
        if (index == -1) {
            // ERROR
            throw new Exception(
                    "Error-Entity-getAttributeType-AttributeNotExist");
        }
        return this.unique[index];
    }

    /************************************************************************/
    @Override
    public boolean equals(Object that) {
        EntityStorage e = (EntityStorage) that;
        if (e.getNome().equals(this.nameEntity)) {
            //è un'istanza dello stesso tipo di entity
            try {
                if (e.nameEntity.equals("User")) {
                    if (this.getAttributeValueNoLimitsToString(this.namePrimaryKey).equals(e.getAttributeValueNoLimitsToString(this.namePrimaryKey))) {
                        if (this.getAttributeValueNoLimitsToString("password").equals(e.getAttributeValueNoLimitsToString("password")) == true) {
                            return true;
                        }
                    }
                } else {
                    return this.getAttributeValueNoLimitsToString(this.namePrimaryKey).equals(e.getAttributeValueNoLimitsToString(this.namePrimaryKey));
                }
            } catch (Exception ex) {
            }
        }
        return false;
    }

    /**
     * Questo metotdo ritorna true se il tipo è compatibile false altrimenti
     *
     * @param index
     *            indice dell'attributo presente all'interno della classe
     * @param AttributeValue
     *            valore dell'attributo da controllare, passato dall'esterno
     * @return true se il tipo è compatibile false altrimenti
     * @throws Exception
     */
    public String checkAttribute(String attributeName, String AttributeValue) {
        int index;
        boolean isCompatible = false, isUnique = false;
        index = this.getAttributeIndex(attributeName);
        if (this.inserible[index].equals("FORCED")) {
            //controllo inserimento forzato
            if (AttributeValue.length() == 0) {
                return "mustBeInsert";
            }
        }
        String type = this.typeAttribute[index], type2[];
        type = type.trim();
        type2 = type.split(":");

        //inizio controllo compatibilità tipo
        if ((type.equals("String")) || (type2[0].trim().equals("Password"))) {
            isCompatible = true;
        }

        if (type.equals("Number")) {
            try {
                Integer.parseInt(AttributeValue);
                isCompatible = true;
            } catch (Exception e) {
                isCompatible = false;
            }
        }

        if (type.equals("Decimal")) {
            try {
                Float.parseFloat(AttributeValue);
                isCompatible = true;
            } catch (Exception e) {
                isCompatible = false;
            }
        }
        if (!isCompatible) {
            return "TypeNotCompatible";
        }

        //fine controllo compatibilità tipo

        if (this.unique[index] == true) {
            //controllo unicità
            try {
                StorageInterface storage = new StorageInterface();
                String parameter[] = new String[3];
                parameter[0] = "onlyOne";
                parameter[1] = attributeName;
                parameter[2] = AttributeValue;
                Entity e[] = storage.find(this, parameter);
                if (e.length == 0) {
                    isUnique = true;
                }
            } catch (Exception e) {
                isUnique = true;
            }

            if (!isUnique) {
                return "NotIsUnique";
            }
        }

        return "allOk";
    }
    //******************************************************
    //**************END PUBLIC******************************
    //******************************************************
}
